//
// Created by DELL on 2021/3/10.
//

#include "mutex.h"
#include "macro.h"
#include "scheduler.h"

namespace sylar {
    Semaphore::Semaphore(uint32_t count) {
        if (sem_init(&m_semaphore, 0, count)) {
            throw std::logic_error("sem_init error");
        }
    }

    Semaphore::~Semaphore() {
        sem_destroy(&m_semaphore);
    }

    void Semaphore::wait() {
        if (sem_wait(&m_semaphore)) {
            throw std::logic_error("sem_wait error");
        }
    }

    void Semaphore::notify() {
        if (sem_post(&m_semaphore)) {
            throw std::logic_error("sem_post error");
        }
    }


    FiberSemaphore::FiberSemaphore(size_t initial_concurrency)
            :m_concurrency(initial_concurrency) {
    }

    FiberSemaphore::~FiberSemaphore() {
        SYLAR_ASSERT(m_waiters.empty());
    }

    bool FiberSemaphore::tryWait() {
        SYLAR_ASSERT(Scheduler::GetThis());
        {
            MutexType::Lock lock(m_mutex);
            if(m_concurrency > 0u) {
                --m_concurrency;
                return true;
            }
            return false;
        }
    }

    void FiberSemaphore::wait() {
        SYLAR_ASSERT(Scheduler::GetThis());
        {
            MutexType::Lock lock(m_mutex);
            if(m_concurrency > 0u) {
                --m_concurrency;
                return;
            }
            m_waiters.push_back(std::make_pair(Scheduler::GetThis(), Fiber::GetThis()));
        }
        Fiber::YieldToHold();
    }

    void FiberSemaphore::notify() {
        MutexType::Lock lock(m_mutex);
        if(!m_waiters.empty()) {
            auto next = m_waiters.front();
            m_waiters.pop_front();
            next.first->schedule(next.second);
        } else {
            ++m_concurrency;
        }
    }
}